'    Copyright 2008 Jan Molnar <jan.molnar@abds.de>
'
'    This file is part of JCQ.

'    JCQ is free software: you can redistribute it and/or modify
'    it under the terms of the GNU General Public License as published by
'    the Free Software Foundation, either version 2 of the License, or
'    (at your option) any later version.

'    JCQ is distributed in the hope that it will be useful,
'    but WITHOUT ANY WARRANTY; without even the implied warranty of
'    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
'    GNU General Public License for more details.

'    You should have received a copy of the GNU General Public License
'    along with JCQ. If not, see <http://www.gnu.org/licenses/>.

Public Class MessageWindowViewModel
    Implements System.ComponentModel.INotifyPropertyChanged

    Private _Messages As System.Collections.ObjectModel.ObservableCollection(Of MessageViewModel)
    Private _ReadOnlyMessages As System.Collections.ObjectModel.ReadOnlyObservableCollection(Of MessageViewModel)

    Private _Contact As ContactViewModel

    Private _ContactImage As ImageSource
    Private _ContactImageCreated As Boolean

    Private WithEvents _TypingNotificationTimer As CountDownTimer

    Public Event ConatctInformationWindowRequested As EventHandler
    Public Event ContactHistoryWindowRequested As EventHandler
    Public Event ShowRequested As EventHandler

    Public Sub New(ByVal contact As ContactViewModel)
        _Contact = contact

        ApplicationService.Current.Context.GetService(Of IcqInterface.Interfaces.IUserInformationService).RequestShortUserInfo(contact.Model)

        _Messages = New System.Collections.ObjectModel.ObservableCollection(Of MessageViewModel)
        _ReadOnlyMessages = New System.Collections.ObjectModel.ReadOnlyObservableCollection(Of MessageViewModel)(_Messages)

        ApplicationService.Current.Context.GetService(Of IcqInterface.Interfaces.IIconService).RequestContactIcon(contact.Model)

        _TypingNotificationTimer = New CountDownTimer(TimeSpan.FromSeconds(6))
    End Sub

    Public ReadOnly Property Contact() As ContactViewModel
        Get
            Return _Contact
        End Get
    End Property

    Public ReadOnly Property Messages() As System.Collections.ObjectModel.ReadOnlyObservableCollection(Of MessageViewModel)
        Get
            Return _ReadOnlyMessages
        End Get
    End Property

    Private _StatusText As String
    Public Property StatusText() As String
        Get
            Return _StatusText
        End Get
        Set(ByVal value As String)
            _StatusText = value
            OnPropertyChanged("StatusText")
        End Set
    End Property

    Public Shared Sub RegisterEventHandlers()
        Dim svMessaging As IcqInterface.Interfaces.IMessageService
        Dim svNotification As IcqInterface.Interfaces.INotificationService
        Dim svStorage As IcqInterface.Interfaces.IStorageService

        svMessaging = ApplicationService.Current.Context.GetService(Of IcqInterface.Interfaces.IMessageService)()
        svNotification = ApplicationService.Current.Context.GetService(Of IcqInterface.Interfaces.INotificationService)()
        svStorage = ApplicationService.Current.Context.GetService(Of IcqInterface.Interfaces.IStorageService)()

        AddHandler svMessaging.MessageReceived, AddressOf OnMessageReceived
        AddHandler svNotification.TypingNotificationEventArgs, AddressOf OnTypingNotification
        AddHandler svStorage.ContactStatusChanged, AddressOf OnContactStatusChanged
    End Sub

#Region " Shared Event Handlers "

    Private Shared Sub OnMessageReceived(ByVal sender As Object, ByVal e As IcqInterface.Interfaces.MessageEventArgs)
        Try
            Application.Current.Dispatcher.Invoke(Windows.Threading.DispatcherPriority.Normal, New Action(Of IcqInterface.Interfaces.IMessage)(AddressOf ProcessMessageReceived), e.Message)
        Catch ex As Exception
            Core.Kernel.Exceptions.PublishException(ex)
        End Try
    End Sub

    Private Shared Sub OnContactStatusChanged(ByVal sender As Object, ByVal e As IcqInterface.Interfaces.StatusChangedEventArgs)
        Try
            Application.Current.Dispatcher.Invoke(Windows.Threading.DispatcherPriority.Normal, New Action(Of IcqInterface.Interfaces.StatusChangedEventArgs)(AddressOf ProcessStatusChanged), e)
        Catch ex As Exception
            Core.Kernel.Exceptions.PublishException(ex)
        End Try
    End Sub

    Private Shared Sub OnTypingNotification(ByVal sender As Object, ByVal e As IcqInterface.Interfaces.TypingNotificationEventArgs)
        Try
            Application.Current.Dispatcher.Invoke(Windows.Threading.DispatcherPriority.Normal, New Action(Of IcqInterface.Interfaces.TypingNotificationEventArgs)(AddressOf ProcessTypingNotification), e)
        Catch ex As Exception
            Core.Kernel.Exceptions.PublishException(ex)
        End Try
    End Sub

#End Region

#Region " Shared Event Processors "

    Private Shared Sub ProcessMessageReceived(ByVal msg As IcqInterface.Interfaces.IMessage)
        Dim vm As MessageWindowViewModel
        Dim contact As ContactViewModel

        Dim offlineMsg As IcqInterface.IcqOfflineMessage
        Dim message As TextMessageViewModel

        Try
            contact = ContactViewModelCache.GetViewModel(msg.Sender)

            vm = ApplicationService.Current.Context.GetService(Of IContactWindowViewModelService).GetMessageWindowViewModel(contact)

            offlineMsg = TryCast(msg, IcqInterface.IcqOfflineMessage)

            If offlineMsg IsNot Nothing Then
                message = New OfflineTextMessageViewModel(offlineMsg, MessageColors.Contact1Color)
            Else
                message = New TextMessageViewModel(msg, MessageColors.Contact1Color)
            End If

            vm.DisplayMessage(message)
            vm.Show()
        Catch ex As Exception
            Core.Kernel.Exceptions.PublishException(ex)
        End Try
    End Sub

    Private Shared Sub ProcessStatusChanged(ByVal e As IcqInterface.Interfaces.StatusChangedEventArgs)
        Dim vm As MessageWindowViewModel
        Dim contact As ContactViewModel
        Dim message As StatusChangedMessageViewModel

        Try
            contact = ContactViewModelCache.GetViewModel(e.Contact)

            If ApplicationService.Current.Context.GetService(Of IContactWindowViewModelService).IsMessageWindowViewModelAvailable(contact) Then
                vm = ApplicationService.Current.Context.GetService(Of IContactWindowViewModelService).GetMessageWindowViewModel(contact)

                message = New StatusChangedMessageViewModel(Date.Now, contact, vm.Contact, e.Status, MessageColors.SystemColor)

                vm.DisplayStatus(message)
            End If
        Catch ex As Exception
            Core.Kernel.Exceptions.PublishException(ex)
        End Try
    End Sub

    Private Shared Sub ProcessTypingNotification(ByVal e As IcqInterface.Interfaces.TypingNotificationEventArgs)
        Dim vm As MessageWindowViewModel
        Dim contact As ContactViewModel

        Try
            contact = ContactViewModelCache.GetViewModel(e.Contact)

            If ApplicationService.Current.Context.GetService(Of IContactWindowViewModelService).IsMessageWindowViewModelAvailable(contact) Then
                vm = ApplicationService.Current.Context.GetService(Of IContactWindowViewModelService).GetMessageWindowViewModel(contact)
                vm.DisplayTypingNotification(e.NotificationType)
            End If
        Catch ex As Exception
            Core.Kernel.Exceptions.PublishException(ex)
        End Try
    End Sub

#End Region

    Public Sub RequestContactInformationWindow()
        RaiseEvent ConatctInformationWindowRequested(Me, EventArgs.Empty)
    End Sub

    Public Sub RequestChatHistoryWindow()
        RaiseEvent ContactHistoryWindowRequested(Me, EventArgs.Empty)
    End Sub

    Public Sub SendMessage(ByVal text As String)
        Dim msg As IcqInterface.IcqMessage
        Dim message As TextMessageViewModel

        msg = New IcqInterface.IcqMessage
        msg.Sender = ApplicationService.Current.Context.Identity
        msg.Recipient = Contact.Model
        msg.Text = text

        ApplicationService.Current.Context.GetService(Of IcqInterface.Interfaces.IMessageService).SendMessage(msg)

        message = New TextMessageViewModel(msg, MessageColors.IdentityColor)

        DisplayMessage(message)
    End Sub

    Public Sub Show()
        RaiseEvent ShowRequested(Me, EventArgs.Empty)
    End Sub

    Public Sub Close()
        ApplicationService.Current.Context.GetService(Of IContactWindowViewModelService).RemoveMessageWindowViewModel(Contact)
    End Sub

    Private Sub DisplayTypingNotification(ByVal status As IcqInterface.Interfaces.NotificationType)
        Select Case status
            Case IcqInterface.Interfaces.NotificationType.TypingFinished
                StatusText = String.Format("{0} finished typing a message.", Contact.Name)
            Case IcqInterface.Interfaces.NotificationType.TypingStarted
                StatusText = String.Format("{0} is typing a message.", Contact.Name)
            Case IcqInterface.Interfaces.NotificationType.TypingCanceled
                StatusText = String.Format("{0} cleared the entered text.", Contact.Name)
        End Select
    End Sub

    Private Function GetSenderRole(ByVal message As IcqInterface.Interfaces.IMessage) As MessageSenderRole
        If message.Sender.Identifier = ApplicationService.Current.Context.Identity.Identifier Then
            Return MessageSenderRole.ContextIdentity
        Else
            Return MessageSenderRole.FirstAtt
        End If
    End Function

    Private Sub DisplayMessage(ByVal message As TextMessageViewModel)
        'Paragraph = Core.Kernel.Services.GetService(Of IMessageFormattingService).FormatMessage(GetSenderRole(message), message)
        ApplicationService.Current.Context.GetService(Of IContactHistoryService).AppendMessage(Contact, message)

        If message.Sender Is Contact Then
            StatusText = String.Format("Last message received on {0}.", message.DateCreated.ToShortTimeString)
        Else
            StatusText = String.Format("Last message sent on {0}.", message.DateCreated.ToShortTimeString)
        End If

        _Messages.Add(message)
        'Document.Blocks.Add(paragraph)

        RaiseEvent DocumentScrollRequired(Me, EventArgs.Empty)
    End Sub

    Private Sub DisplayStatus(ByVal message As StatusChangedMessageViewModel)
        'Dim paragraph As Paragraph

        'paragraph = Core.Kernel.Services.GetService(Of IMessageFormattingService).FormatStatus(Contact, status)

        'Document.Blocks.Add(paragraph)

        _Messages.Add(message)
    End Sub

    Private textChangedTimer As System.Windows.Threading.DispatcherTimer

    Public Sub SendTypingNotification(ByVal action As TextChangedAction)
        Select Case action
            Case TextChangedAction.Changed
                If _TypingNotificationTimer.IsRunning Then
                    _TypingNotificationTimer.Reset()
                Else
                    _TypingNotificationTimer.Start()
                    SendTypingNotification(IcqInterface.Interfaces.NotificationType.TypingStarted)
                End If
            Case TextChangedAction.Cleared
                _TypingNotificationTimer.Stop()
                SendTypingNotification(IcqInterface.Interfaces.NotificationType.TypingCanceled)
        End Select
    End Sub

    ''' <summary>
    ''' Sends a notification that the user is typing something, has finished typing and cleared his text.
    ''' </summary>
    Private Sub SendTypingNotification(ByVal type As IcqInterface.Interfaces.NotificationType)
        Dim svNotification As IcqInterface.Interfaces.INotificationService

        svNotification = ApplicationService.Current.Context.GetService(Of IcqInterface.Interfaces.INotificationService)()

        svNotification.SendNotification(Contact.Model, type)
    End Sub

    Protected Sub OnPropertyChanged(ByVal propertyName As String)
        RaiseEvent PropertyChanged(Me, New System.ComponentModel.PropertyChangedEventArgs(propertyName))
    End Sub

    Public Event DocumentScrollRequired As EventHandler

    Public Event PropertyChanged(ByVal sender As Object, ByVal e As System.ComponentModel.PropertyChangedEventArgs) Implements System.ComponentModel.INotifyPropertyChanged.PropertyChanged

    Private Sub OnTypingNotificationTimer_Tick(ByVal sender As Object, ByVal e As System.EventArgs) Handles _TypingNotificationTimer.Tick
        Try
            SendTypingNotification(IcqInterface.Interfaces.NotificationType.TypingFinished)
        Catch ex As Exception
            Core.Kernel.Exceptions.PublishException(ex)
        End Try
    End Sub
End Class

Public Class CountDownTimer
    Implements IDisposable

    Private _timer As System.Threading.Timer

    Public Event Tick As EventHandler

    Public Sub New(ByVal countDownDue As TimeSpan)
        Dim actualMilliseconds As Double

        actualMilliseconds = Math.Round(countDownDue.TotalMilliseconds)

        If actualMilliseconds > Int32.MaxValue Then Throw New ArgumentOutOfRangeException("countDownDue", "No more than Int32.MaxValue milliseconds.")

        _CountDownDue = Convert.ToInt32(actualMilliseconds)

        _timer = New System.Threading.Timer(AddressOf OnTimerCallback, Nothing, System.Threading.Timeout.Infinite, System.Threading.Timeout.Infinite)
    End Sub

    Private Sub OnTimerCallback(ByVal state As Object)
        Me.Stop()
        RaiseEvent Tick(Me, EventArgs.Empty)
    End Sub

    Private _CountDownDue As Integer

    Private _IsRunning As Boolean
    Public ReadOnly Property IsRunning() As Boolean
        Get
            Return _IsRunning
        End Get
    End Property

    Public Sub Start()
        _timer.Change(_CountDownDue, System.Threading.Timeout.Infinite)
        _IsRunning = True
    End Sub

    Public Sub Reset()
        _timer.Change(_CountDownDue, System.Threading.Timeout.Infinite)
    End Sub

    Public Sub [Stop]()
        _timer.Change(System.Threading.Timeout.Infinite, System.Threading.Timeout.Infinite)
        _IsRunning = False
    End Sub

    Private disposedValue As Boolean = False        ' To detect redundant calls

    ' IDisposable
    Protected Overridable Sub Dispose(ByVal disposing As Boolean)
        If Not Me.disposedValue And disposing Then
            If _timer IsNot Nothing Then _timer.Dispose()
        End If
        Me.disposedValue = True
    End Sub

#Region " IDisposable Support "
    ' This code added by Visual Basic to correctly implement the disposable pattern.
    Public Sub Dispose() Implements IDisposable.Dispose
        ' Do not change this code.  Put cleanup code in Dispose(ByVal disposing As Boolean) above.
        Dispose(True)
        GC.SuppressFinalize(Me)
    End Sub
#End Region

End Class

Public Enum TextChangedAction
    Changed
    Cleared
End Enum

Public Class MessageColors
    Public Shared ReadOnly Property IdentityColor() As Brush
        Get
            Return Brushes.Red
        End Get
    End Property

    Public Shared ReadOnly Property SystemColor() As Brush
        Get
            Return Brushes.Gray
        End Get
    End Property

    Public Shared ReadOnly Property HistoryColor() As Brush
        Get
            Return Brushes.Gray
        End Get
    End Property

    Public Shared ReadOnly Property Contact1Color() As Brush
        Get
            Return Brushes.Blue
        End Get
    End Property

    Public Shared ReadOnly Property Contact2Color() As Brush
        Get
            Return Brushes.Green
        End Get
    End Property

    Public Shared ReadOnly Property Contact3Color() As Brush
        Get
            Return Brushes.Orange
        End Get
    End Property

    Public Shared ReadOnly Property Contact4Color() As Brush
        Get
            Return Brushes.Purple
        End Get
    End Property
End Class